package dnet

import (
	"dgo/goutils/utils"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type TcpCltEvent func(sender *TcpClt)
type TcpCltBufEvent func(sender *TcpClt, buf []byte)
type TcpClt struct {
	startFlag     int32
	terminated    byte
	connectedflag int
	conn          net.Conn
	addr          string
	closeW        sync.WaitGroup
	DisReason     string
	connecttime   time.Time
	lastrecv_t    time.Time

	ConnectedFail     func(sender *TcpClt, err error)
	AfterRecvBuf      TcpCltBufEvent
	AfterConnected    TcpCltEvent
	AfterDisconnected TcpCltEvent
	OnMakeRecvBufFunc func() []byte

	BeforeConnect func(sender *TcpClt) bool

	RecvMonitor *utils.MonitorSpeedSizeRec
	SendMonitor *utils.MonitorSpeedSizeRec
}

func NewTcpClt(addr string) *TcpClt {
	rval := &TcpClt{
		addr: addr,
	}
	return rval
}

func (this *TcpClt) GetRemoteAddr() string {
	return this.addr
}

func (this *TcpClt) SetAddress(addr string) bool {
	if this.addr != addr {
		this.addr = addr
		return true
	}
	return false
}

func (this *TcpClt) Start() {
	if atomic.CompareAndSwapInt32(&this.startFlag, 0, 1) {
		this.terminated = 0
		this.closeW.Add(1)
		utils.GoFunc2(this.handleRecv)
	}
}

func (this *TcpClt) Stop() {
	if this.startFlag != 1 {
		return
	}
	this.terminated = 1
	this.RequestDisconnect("STOP")
	this.closeW.Wait()
}

func (this *TcpClt) Active() bool {
	return this.startFlag == 1
}

func (this *TcpClt) Connected() bool {
	return this.connectedflag == 1
}

func (this *TcpClt) RequestDisconnect(reason string) {

	if this.connectedflag == 1 {
		this.DisReason = reason
		this.connectedflag = 0
	}
	if this.conn != nil {
		this.conn.Close()
		return
	}
	return
}

/*
**

	检测是否超时
	 false 不需要断线, 没有超时, 或者没有连接
*/
func (this *TcpClt) CheckRecvTimeOut(timeout time.Duration) bool {
	if !this.Connected() {
		return false
	}

	if time.Now().Sub(this.connecttime) < timeout {
		return false
	}

	return time.Now().Sub(this.lastrecv_t) > timeout
}

func (this *TcpClt) connect() error {
	conn, err := net.Dial("tcp", this.addr)
	if err != nil {
		return err
	}
	this.conn = conn
	this.connectedflag = 1
	this.connecttime = time.Now()
	if this.AfterConnected != nil {
		this.AfterConnected(this)
	}
	return nil
}

func (this *TcpClt) checkConnected() bool {
	if len(this.addr) == 0 {
		return false
	}

	if this.connectedflag == 0 {
		if this.BeforeConnect != nil {
			if !this.BeforeConnect(this) {
				return false
			}
		}
		err := this.connect()
		if err != nil {
			if this.ConnectedFail != nil {
				this.ConnectedFail(this, err)
			}
		}
	}
	return this.conn != nil
}

func (this *TcpClt) Write(buffer []byte) (int, error) {
	conn := this.conn
	if conn == nil {
		return -1, fmt.Errorf("connection is not connected")
	}
	n, err := conn.Write(buffer)
	if err == nil {
		if this.SendMonitor != nil {
			this.SendMonitor.Delta(int64(n))
		}
	} else {
		this.RequestDisconnect(err.Error())
	}
	return n, err
}

func (this *TcpClt) handleRecv() {
	defer func() {
		this.closeW.Done()
		this.startFlag = 0
	}()
	recvBufFnc := this.OnMakeRecvBufFunc
	if recvBufFnc == nil {
		recvBufFnc = func() []byte {
			return make([]byte, 1024)
		}
	}

	for this.terminated == 0 {
		if !this.checkConnected() {
			time.Sleep(time.Second)
			continue
		}
		recvBuf := recvBufFnc()
		n, err := this.conn.Read(recvBuf)
		if err != nil {
			this.RequestDisconnect(err.Error())
			if this.AfterDisconnected != nil {
				this.AfterDisconnected(this)
			}
			this.conn = nil
			continue
		}

		this.lastrecv_t = time.Now()
		if this.RecvMonitor != nil {
			this.RecvMonitor.Delta(int64(n))
		}

		if this.AfterRecvBuf != nil {
			this.AfterRecvBuf(this, recvBuf[:n])
		}
	}
}
